Skip to content

Conversation

@thesamesam
Copy link
Member

@thesamesam thesamesam commented Nov 25, 2025

Detect the basic case of an ignored eclass phase where two eclasses are
inherited, both defining the same phase, and the ebuild does not define
a custom implementation of that phase at all. This means one of the exported
phases from the eclasses is being ignored.

Ignore some eclasses with a blacklist where they are known to vary their
API by EAPI or eclass variables, at least for now, because the eclass
cache we have accessible here isn't keyed by EAPI or the context of the
sourcing ebuild.

Bug: https://bugs.gentoo.org/516014
Bug: https://bugs.gentoo.org/795006
Closes: #377
Signed-off-by: Sam James sam@gentoo.org

@thesamesam thesamesam force-pushed the sam-inherit-pkg_setup branch 11 times, most recently from 2cfe29e to 91afa80 Compare November 26, 2025 01:08
Detect the basic case of an ignored eclass phase where two eclasses are
inherited, both defining the same phase, and the ebuild does not define
a custom implementation of that phase at all. This means one of the exported
phases from the eclasses is being ignored.

Ignore some eclasses with a blacklist where they are known to vary their
API by EAPI or eclass variables, at least for now, because the eclass
cache we have accessible here isn't keyed by EAPI or the context of the
sourcing ebuild.

Bug: https://bugs.gentoo.org/516014
Bug: https://bugs.gentoo.org/795006
Closes: pkgcore#377
Signed-off-by: Sam James <sam@gentoo.org>
Signed-off-by: Sam James <sam@gentoo.org>
@thesamesam thesamesam force-pushed the sam-inherit-pkg_setup branch from 91afa80 to 10489c0 Compare November 26, 2025 01:30
@thesamesam
Copy link
Member Author

thesamesam commented Nov 26, 2025

There's a lot of noise from things like cargo.eclass where we only use it for setting up flags but we don't want the other phases sometimes, ditto go. Not sure how to handle that (a heuristic would be fine).

One might argue this really means we should have xyz-utils.eclass more often rather than making an eclass do many things, sometimes with toggles to customise it.

We can also have this be off-by-default, but nonetheless, better heuristics would be useful..

Copy link
Contributor

@ferringb ferringb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

random code poking. Take what makes sense, discard what doesn't. Y'all know this code better than I do.

# We could maybe make this more finely-grained for phases we know
# are conditionally exported if this list is impacting coverage
# severely.
blacklisted_eclasses = ["pypi", "vala", "xdg"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a class level frozenset, so it can be varied to test in the testcases. Plus it's more obvious.

def __init__(self, phase, providers, **kwargs):
super().__init__(**kwargs)
self.phase = phase
self.providers = tuple(providers)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

self.providers = tuple(sorted(providers)); expect the invokers of this class will forget to sort, so just force it on them. Mark providers as typing.Iterable[str]- assuming it's a string?

And yes, I'm now brutally anal about typing. The syntax sucks, but it's better devex and I'd like to eventually enforce mypy.

phase for phase, eclass in exported_phases.items() if len(eclass) > 1
) - set(defined_phases)

for missing in missing_custom_phases:
Copy link
Contributor

@ferringb ferringb Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this code is equivalent to above.

# use the pkg's eapi, not latest.  Conflicts in EAPI's the pkg isn't, don't matter for it until it is that EAPI/
possible_conflicts = set(pkg.eapi.phases_rev)
# strip out what the pkg defines
for node in bash.func_query.captures(pkg.tree.root_node).get("func", ()):
    func_name = pkg.node_str(node.child_by_field_name("name"))
    possible_conflicts.discard(func_name)

for phase in possible_conflicts:
  # iterate the remaining phases, finding where there are multiple eclasses providing this phase.
  eclass_sources = []
  for eclasses, _ in inherits:
    for eclass in eclasses:
      # I assume .functions supports __contains__, and isn't strictly typing.Iterable[str]
      if f'{eclass}_phase' in eclass.functions:
        eclass_sources.append(eclass)
  if len(eclass_sources) > 1:
    yield ShadowedEclassPhase(phase, eclass_sources, pkg=pkg)

I'd also rename providers to eclasses in ShadowedEclassPhase to be clearer.

# Strip out phases we already define (even if inside of those, we don't
# actually call exported phases from all eclasses inherited). Assume that
# a custom phase in the ebuild is intentionally omitting them.
missing_custom_phases = set(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also filter out eclasses where of those defining the phase, they have @PROVIDES which has the phase (because it might wrap the eclass and call that phase for you).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Detect "shadowed" pkg_{setup,pretend} phases

2 participants